#include <iostream>
#include <vector>
#include <string>
#include <cstdlib>
#include <cstring>
#include <cassert>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "Task.hpp" //任务所需头文件

using namespace std;

#define NAME_SIZE 64

// 封装一个包含各种必备信息的类
class ProcInfo
{
public:
    ProcInfo(pid_t id = pid_t(), int fd = int())
        : _childID(id), _wfd(fd), _num(++_count)
    {
        char buff[NAME_SIZE];
        snprintf(buff, sizeof buff, "Process %d [%d:%d]", _num, _childID, _wfd);
        _name = string(buff);
    }

    ~ProcInfo()
    {
        _childID = _wfd = 0;
    }

    pid_t _childID;    // pid
    int _wfd;          // 写端 fd
    string _name;      // 进程名
    int _num;          // 编号
    static int _count; // 计数
};

int ProcInfo::_count = 0; // 静态成员初始化

void waitCommand(int rfd)
{
    while (true)
    {
        // 读端尝试读取信息
        int command = 0;
        int n = read(rfd, &command, sizeof(command));
        if (n != 0)
        {
            TaskPools().Execute(command);
        }
        else
        {
            cout << "当前子进程读取任务失败，已退出!" << endl;
            break;
        }
    }
}

void CreateProcessAndPipe(vector<ProcInfo> &PP, int ppNum = 3)
{
    vector<int> fds; // 存储继承中不需要的写端 fd
    for (int i = 0; i < ppNum; i++)
    {
        // 首先创建管道
        int pipefd[2];
        int ret = pipe(pipefd);
        assert(ret != -1);
        (void)ret;

        // 然后创建子进程
        int id = fork();
        assert(id != -1);
        (void)id;

        if (id == 0)
        {
            // 子进程内

            // 需要先关闭之前子进程遗留的写端fd
            for (auto e : fds)
                close(e);

            close(pipefd[1]);       // 子进程关闭写端
            waitCommand(pipefd[0]); // 子进程等待命令

            close(pipefd[0]);
            exit(0);
        }

        // 父进程内
        close(pipefd[0]); // 父进程关闭读端
        PP.push_back(ProcInfo(id, pipefd[1]));
        fds.push_back(pipefd[1]);
    }
}

void showTask()
{
    cout << "**************************" << endl;
    cout << "* 0.日志打印  1.数据插入 *" << endl;
    cout << "* 2.网络请求  3.退出程序 *" << endl;
    cout << "**************************" << endl;
}

void CtrlProcess(vector<ProcInfo> &PP)
{
    while (true)
    {
        // 展示当前可用的进程
        int index = 0;
        do
        {
            cout << "当前可选择进程：";
            for (int i = 1; i <= PP.size(); i++)
                cout << i << " ";
            cout << endl;

            cout << "请选择进程: ";
            cin >> index;
        } while (index < 1 || index > PP.size());

        int taskNum = 0;
        do
        {
            showTask(); // 展示可选任务
            cout << "请选择任务: ";
            cin >> taskNum;
        } while (taskNum < 0 || taskNum > 3);

        // 分配任务
        if (taskNum == 3)
            break;

        cout << "已选择： " << PP[index - 1]._num << " 号进程 | " << PP[index - 1]._name << endl;
        write(PP[index - 1]._wfd, &taskNum, sizeof(taskNum));
        sleep(1); // 执行完任务后，睡一会
    }
}

void WaitProcess(vector<ProcInfo> &PP)
{
    // 遍历回收就好了
    for (auto e : PP)
    {
        close(e._wfd);  //关闭写端，读端读取到 0 自动结束阻塞
        int status = 0;
        waitpid(e._childID, &status, 0);

        // 通过 status 判断子进程运行情况
        if ((status & 0x7F))
        {
            printf("子进程异常退出，core dump: %d   退出信号：%d\n", (status >> 7) & 1, (status & 0x7F));
        }
        else
        {
            printf("子进程 %s 正常退出，退出码：%d\n", e._name.c_str(), (status >> 8) & 0xFF);
        }
    }

    cout << "所有子进程都已回收" << endl;
}

int main()
{
    // 1、创建一批进程及匿名管道
    vector<ProcInfo> PP;
    CreateProcessAndPipe(PP, 5);

    // 2、进程控制
    CtrlProcess(PP);

    // 3、进程回收
    WaitProcess(PP);

    return 0;
}